package com.service.scheduling.task;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.service.dao.enums.CoinsEnum;
import com.service.dao.enums.CommonStatusEnums;
import com.service.dao.enums.MemberJAType;
import com.service.dao.model.ConvertCoinsRecord;
import com.service.dao.model.ConvertConfig;
import com.service.dao.model.MemberStore;
import com.service.scheduling.dto.ConversionConfigVo;
import com.service.scheduling.dto.CrossCurrencyVO;
import com.service.scheduling.dto.MemberStoreChange;
import com.service.scheduling.service.*;
import com.service.scheduling.support.MemberStoreLockUtils;
import com.service.scheduling.utils.HttpUtil;
import com.service.scheduling.utils.MathUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
@Component
public class ConvertCoinReleaseTask {

    @Autowired
    private ConvertCoinsRecordService convertRecordService;

    @Autowired
    private ConvertConfigService convertConfigService;

    @Autowired
    private CoinsPriceService coinsPriceService;

    @Autowired
    private MemberStoreService memberStoreService;

    @Autowired
    private MemberStoreLockUtils memberStoreLockUtils;

    @Value("${wallet_service_base_url}")
    private String walletServiceUrl;
    @Value("${wallet_exdf_coin}")
    private String walletExdfCoin;
    @Value("${wallet_exdf_secret}")
    private String walletExdfSecret;

//    @Scheduled(cron = "0 20 0 * * ?")
    public void doScheduled() throws JsonProcessingException {

        Date today = new Date();
        String todayStr = DateFormatUtils.format(today, "yyyy-MM-dd HH:mm:ss");
        LocalDate localDate = LocalDate.now().plusDays(-1);
        String yesterday = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        log.info("今日<{}>释放昨日<{}>合成货币任务-start", todayStr, yesterday);
        List<ConvertConfig> list = convertConfigService.list(new QueryWrapper<ConvertConfig>().lambda().eq(ConvertConfig::getConfigStatus, CommonStatusEnums.ACTIVE.getCode()));
        if (ObjectUtils.isEmpty(list)) {
            log.info("今日<{}>释放昨日<{}>合成货币任务-end 合成配置为空", todayStr, yesterday);
            return;
        }

        CoinsEnum targetCoin = CoinsEnum.valueOf(list.get(0).getTargetCoinsId());
        List<ConvertCoinsRecord> coinsRecordList = convertRecordService.listBySomeDay(targetCoin, localDate);
        Map<String, BigDecimal> coinPrice = coinsPriceService.getStandardPrice2Map(false);

        if (ObjectUtils.isEmpty(coinsRecordList)) {
            log.info("今日<{}>释放昨日<{}>合成货币任务-end 待合成记录为空", todayStr, yesterday);
            return;
        }
        ConversionConfigVo configRep = calculateNumPer(targetCoin, coinPrice);
        BigDecimal coinNumPer = configRep.getCoinsNum();

        Map<String, CrossCurrencyVO> resultMap = Maps.newConcurrentMap();
        coinsRecordList.parallelStream().forEach(item -> {
            BigDecimal coinsNum = item.getConvertNum().multiply(coinNumPer);
            log.info("今日<{}>释放昨日<{}>会员<id:{},account:{}> 合成:{}, 份数:{},收获数量:{}", todayStr, yesterday,
                    item.getMemberId(), item.getMemberAccount(), item.getTargetCoinsName(), item.getConvertNum(), coinsNum);
            //TODO:释放货币到余额

            MemberStore memberStore = memberStoreService.getMemberStore(item.getMemberId(), item.getTargetCoinsName());
            if (memberStoreLockUtils.lock(item.getMemberId(), memberStore.getId())) {
                try {
                    MemberStoreChange memberStoreChange = MemberStoreChange.builder()
                            .changeNum(coinsNum)
                            .coinsId(item.getTargetCoinsId())
                            .coinsName(item.getTargetCoinsName())
                            .memberId(item.getMemberId())
                            .jaType(MemberJAType.CONVERSION)
                            .orderId(item.getId())
                            .remark(MemberJAType.CONVERSION.getMsg())
                            .build();
                    memberStoreService.changeMemberStore(memberStoreChange);

                    convertRecordService.updateById(ConvertCoinsRecord.builder().id(item.getId())
                            .released(CommonStatusEnums.ACTIVE.getCode())
                            .actualReleaseNum(coinsNum)
                            .releaseTime(today).build());
                    String key = item.getMemberId() + "_" + item.getReleaseAddress();
                    if (resultMap.containsKey(key)) {
                        CrossCurrencyVO record = resultMap.get(key);
                        record.setAmounts(record.getAmounts().add(coinsNum));
                    } else {
                        resultMap.put(key, CrossCurrencyVO.builder()
                                .address(item.getReleaseAddress())
                                .amounts(coinsNum).build());
                    }
                } finally {
                    memberStoreLockUtils.unlock(item.getMemberId(), memberStore.getId());
                }
            }
        });
        List<CrossCurrencyVO> crossCurrencyList = Lists.newArrayList();
        for (String key : resultMap.keySet()) {
            crossCurrencyList.add(resultMap.get(key));
        }

        //拼装参数
        if (crossCurrencyList.size() > 0) {
            //执行调用热钱包进行区块网络合成转账操作
//            List<String> address = memberWalletService.getMemberDepositAddress(uids);
            Map<String, Object> conversionParam = new HashMap<>();
            conversionParam.put("coin", walletExdfCoin);
            conversionParam.put("secret", walletExdfSecret);
            conversionParam.put("crossCurrencyList", crossCurrencyList);
            String result = HttpUtil.post(walletServiceUrl+"/eth/synthesis", new ObjectMapper().writeValueAsString(conversionParam));
            //{"desc":"0xf455f31a257759323f0806097c3cfde48da2156fb1b1fb9f7b5aaae4144afced","status":200,"timestamp":1582043404031}
            log.info("合成处理结果{}", result);
        }

        log.info("今日<{}>释放昨日<{}>合成货币任务-end ", todayStr, yesterday);
    }

    private ConversionConfigVo calculateNumPer(CoinsEnum targetCoin, Map<String, BigDecimal> coinPrice) {
        ConversionConfigVo configRep = getConversionCoinsConfig(targetCoin, coinPrice);
        AtomicReference<BigDecimal> totalNum = new AtomicReference<>(BigDecimal.ZERO);
        configRep.getSourceCoins().forEach(item -> {
            totalNum.set(totalNum.get().add(item.getCoinsNum().multiply(item.getCoinsPrice())));
        });
        configRep.setCoinsNum(MathUtil.div(totalNum.get(), coinPrice.get(configRep.getCoinsName())));
        return configRep;
    }

    private ConversionConfigVo getConversionCoinsConfig(CoinsEnum coinsEnum, Map<String, BigDecimal> coinPrice) {
        ConversionConfigVo result = new ConversionConfigVo();
        result.setSourceCoins(new ArrayList<>());
        List<ConvertConfig> configList = listByTargetCoin(coinsEnum);

        configList.forEach(item -> {
            result.setCoinsName(item.getTargetCoinsName());
            result.setCoinsPrice(coinPrice.get(item.getTargetCoinsName()));
            result.getSourceCoins().add(ConversionConfigVo.builder()
                    .coinsName(item.getSourceCoinsName())
                    .coinsPrice(coinPrice.get(item.getSourceCoinsName()))
                    .coinsNum(item.getSourceNum()).build());
        });

        return result;
    }

    private List<ConvertConfig> listByTargetCoin(CoinsEnum coinsEnum) {
        return listByTargetCoinId(coinsEnum.getCode());
    }

    private List<ConvertConfig> listByTargetCoinId(Integer coinsId) {
        return convertConfigService.list(new QueryWrapper<ConvertConfig>().lambda()
                .eq(ConvertConfig::getTargetCoinsId, coinsId)
                .eq(ConvertConfig::getConfigStatus, CommonStatusEnums.ACTIVE.getCode()));
    }
}
